// VideoDecoder.hpp — 客户端：压缩包 AVPacket → 解码为 BGRA（不依赖 Qt）
// 版本：v4.2.0
//
// 主要职责：
//   1) 依据服务器提供的 StreamInfo 映射（DecoderConfig）创建解码器；
//   2) DecodeAllAvailableFrames：送入压缩包并尽可能取出 0~N 帧；
//   3) 输出像素固定为 BGRA（与 Qt::Format_RGB32 内存布局一致，但本文件不依赖 Qt）；
//   4) Flush 的空包 send 独立计数（flush_packets_sent），不与真实包混算；

#ifndef VIDEO_DECODER_HPP
#define VIDEO_DECODER_HPP

#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

extern "C" {
#include <libavutil/avutil.h>     // 基础类型/时间/错误
#include <libavcodec/avcodec.h>   // 解码器
#include <libavutil/time.h>       // 时间
#include <libswscale/swscale.h>   // 像素格式转换（YUV→BGRA）
#include <libavutil/imgutils.h>   // 图像工具
#include <libavutil/pixdesc.h>    // 像素格式描述
}

#include <cstdint>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

class VideoDecoder {
public:
    //========================== 配置与输出帧 ==========================
    // 解码器配置参数，用于初始化 FFmpeg 解码器。
    // 客户端根据服务端提供的流信息（codec_id、width、height、time_base、extradata）来填充。
    struct DecoderConfig {
        AVCodecID codec_id = AV_CODEC_ID_NONE; // 解码器 ID，该字段必须有效，否则解码器无法创建。对应 FFmpeg 的 AVCodecID（如 AV_CODEC_ID_H264、AV_CODEC_ID_HEVC 等）。

        int width = 0;  // 预期视频宽度（像素）。可为 0，表示暂未知宽度，允许首帧解码后再动态确定。
        int height = 0; // 预期视频高度（像素）。可为 0，表示暂未知高度，允许首帧解码后再动态确定。

        AVRational time_base{1, 1}; // 时间基（time_base）。用于将 PTS（时间戳）转换为秒：timestamp = pts * num / den。
        // 例如 RTSP 常用 {1,90000}，表示 1 秒 = 90000 时间单位。

        std::vector<uint8_t> extradata; // 附加数据（SPS / PPS / VPS 等）。这些参数用于解码器初始化，提高首帧成功率，可为空。
    };


    // 解码后的一帧图像（输出格式固定为 BGRA，与 Qt::Format_RGB32 内存布局一致）。
    struct BGRAFrame {
        std::shared_ptr<uint8_t> data; // 连续的 BGRA 像素缓冲区（B, G, R, A 顺序）。使用 shared_ptr 管理，便于跨线程或 UI 层安全使用。

        int width = 0;   // 帧宽度（像素）。
        int height = 0;  // 帧高度（像素）。
        int stride = 0;  // 每行占用的字节数（通常为 width * 4，但不保证）。使用时应按 stride 访问每行。
        size_t data_size = 0; // 帧数据总字节数，通常等于 stride * height。

        int64_t pts = AV_NOPTS_VALUE; // 原始 PTS（Presentation Timestamp）。若未知则为 AV_NOPTS_VALUE。
        double timestamp = -1.0;      // 时间戳（单位：秒），由 PTS 与 time_base 换算得到。若未知则为 -1.0。
        int frame_number = 0;         // 输出帧序号，从 0 开始递增。

        const char* PixelFormat() const { return "BGRA"; } // 返回像素格式名称，固定为 "BGRA"。
    };


    // 解码统计信息，用于调试和性能分析。
    struct Statistics {
        int64_t total_packets_sent = 0;   // 成功发送的真实压缩包数量（不含 flush 空包）。
        int64_t flush_packets_sent = 0;   // flush 空包的发送次数（用于推动解码器输出尾帧）。
        int64_t total_frames_decoded = 0; // 累计成功解码出的帧总数。
        int64_t total_bytes_out = 0;      // 估算输出的总字节数：帧数 * 宽 * 高 * 4（近似值）。
        double  decode_ratio = 0.0;       // 解码效率指标：frames / packets，表示每个包平均产出多少帧。

        void Print(const std::string& prefix = "") const {
            std::cout << prefix << "========== 解码统计 ==========\n";
            std::cout << prefix << "send 成功(真实包): " << total_packets_sent << "\n";
            std::cout << prefix << "send 成功(flush空包): " << flush_packets_sent << "\n";
            std::cout << prefix << "解码帧:  " << total_frames_decoded << "\n";
            std::cout << prefix << "输出字节估算: " << total_bytes_out
                      << " (" << total_bytes_out / 1024 / 1024 << " MB)\n";
            std::cout << prefix << "解码比率: " << decode_ratio << " (frame/packet)\n";
            std::cout << prefix << "=============================\n";
        }
    };


public:
    //========================== 构造/析构 ==========================
    // 构造函数：根据给定的解码配置初始化解码器。
    // 参数 cfg：由上层提供的 DecoderConfig，包含 codec_id、width、height、time_base、extradata 等信息。
    explicit VideoDecoder(const DecoderConfig& cfg)
        : ctx_(nullptr), yuv_(nullptr), tmp_bgra_(nullptr), sws_(nullptr) // 初始化所有 FFmpeg 资源指针为空
        , out_pix_fmt_(AV_PIX_FMT_BGRA)       // 固定输出像素格式为 BGRA
        , src_fmt_last_(AV_PIX_FMT_NONE)      // 最近一次源像素格式，初始为无效
        , frame_cnt_(0), sent_cnt_(0), flush_sent_cnt_(0) // 统计计数清零
        , out_w_(cfg.width), out_h_(cfg.height)           // 预设输出宽高（若为 0，会在首帧时确定）
        , time_base_(cfg.time_base)                       // 保存时间基，用于 PTS → 秒
        , valid_(false)                                   // 初始状态标记为未初始化
    {
        // 初始化解码器。若失败则清理资源并返回。
        if (!Init(cfg)) {
            Cleanup();
            return;
        }

        // 初始化成功后设置标记。
        valid_ = true;
        std::cout << "[VideoDecoder] ✅ 初始化成功（输出 BGRA）\n";
    }

    // 析构函数：负责安全释放解码器资源。
    ~VideoDecoder() {
        if (valid_) {
            // 在销毁前尝试执行一次 Flush()，将缓存帧尽量取出，以保证统计完整。
            auto rest = Flush();
            (void)rest; // 忽略返回值，只为触发解码器内部刷新。

            // 打印解码统计信息（包数、帧数、解码比率等）。
            PrintStatistics();
        }

        // 释放所有分配的 FFmpeg 资源（上下文、帧缓冲、SWS 转换器等）。
        Cleanup();
    }

    // 拷贝构造与赋值操作禁用。
    // VideoDecoder 管理底层 FFmpeg 指针，不允许复制以避免重复释放或资源共享错误。
    VideoDecoder(const VideoDecoder&) = delete;
    VideoDecoder& operator=(const VideoDecoder&) = delete;

    // 判断解码器是否已成功初始化。
    // 若返回 true，则说明可以安全调用 DecodeAllAvailableFrames() 与 Flush()。
    bool IsValid() const { return valid_; }


    //========================== 解码 API ==========================

    /**
     * @brief 送入一个压缩包并尽可能取出可用帧（0~N）
     * 
     * 关键 FFmpeg API 说明：
     * - avcodec_send_packet(ctx, pkt)
     *   功能：把一个压缩包送入解码流水线；内部可能缓存/重排。
     *   成功：返回 0；失败：负错误码（EAGAIN 表示应先 receive；EOF 表示流结束）。
     *
     * - avcodec_receive_frame(ctx, frame)
     *   功能：从解码器取回解码后的原始帧（可能一次取到多帧）。
     *   返回：0 表示成功拿到一帧；EAGAIN/EOF 表示当前无帧可取/结束；其他负值为错误。
     */
    // 调用 DecodeAllAvailableFrames(pkt)
    // ├─ send(pkt)
    // │   ├─ 返回 EAGAIN → receive* → 重试 send(pkt)
    // │   └─ 其他失败（非 EOF）→ 打错 & 返回已有帧
    // ├─ 计数 sent_cnt_++
    // └─ receive 循环拿 0~N 帧
    //     ├─ EnsureSwsReady（必要时重建）
    //     └─ ConvertToBGRA → out.push_back()
    std::vector<std::shared_ptr<BGRAFrame>> DecodeAllAvailableFrames(const AVPacket* pkt) {
        std::vector<std::shared_ptr<BGRAFrame>> out; // 1. 创建解码帧的容器，返回 out（可能 0~N 帧）
        if (!valid_) { std::cerr << "[VideoDecoder] 未初始化\n"; return out; }  // 解码器未初始化
        if (!pkt)     { std::cerr << "[VideoDecoder] packet 为空\n"; return out; } // pkt 为空

        // 2. send 阶段：把压缩包塞进解码器
        int ret = avcodec_send_packet(ctx_, pkt);
        if (ret == AVERROR(EAGAIN)) {
            // EAGAIN 说明“解码器的输入队列满了，需要先把可取的输出帧取走，腾空间”
            // 进入一个 receive 循环，把能拿到的帧都先拿出来并转换
            while (true) {
                ret = avcodec_receive_frame(ctx_, yuv_); // 收到一个帧
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; // 这轮没有更多帧可取
                if (ret < 0) { PrintFFmpegError("drain(receive) 失败", ret); break; } // 其他错误

                // 成功拿到一个帧，确保像素转换器 SWS 可用
                if (!EnsureSwsReady(yuv_->width, yuv_->height, (AVPixelFormat)yuv_->format)) {
                    std::cerr << "[VideoDecoder] 建立/重建 SWS 失败\n"; continue;
                }
                auto f = ConvertToBGRA(*yuv_); // 把这帧转成 BGRA 并深拷贝
                if (f) out.push_back(std::move(f));
            }
            // 再尝试把“同一包”送进去（只重试一次）
            ret = avcodec_send_packet(ctx_, pkt);
        }
        if (ret < 0) {
            if (ret != AVERROR_EOF) PrintFFmpegError("send 失败", ret);
            return out;
        }
        ++sent_cnt_;

        // —— 取帧（可能 0~N）
        while (true) {
            ret = avcodec_receive_frame(ctx_, yuv_);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
            if (ret < 0) { PrintFFmpegError("receive 失败", ret); break; }

            if (!EnsureSwsReady(yuv_->width, yuv_->height, (AVPixelFormat)yuv_->format)) {
                std::cerr << "[VideoDecoder] 建立/重建 SWS 失败\n";
                continue;
            }
            auto f = ConvertToBGRA(*yuv_);
            if (f) out.push_back(std::move(f));
        }
        return out;
    }

    /**
     * @brief 刷新解码器：送空包把内部缓存帧尽量吐出（0~N）
     * 
     * 说明：
     * - avcodec_send_packet(ctx, nullptr) 表示“无更多输入”，推动解码器输出缓冲帧；
     * - 本函数把“空包 send 成功”计入 flush_packets_sent，而非 total_packets_sent。
     */
    // 调用 Flush()
    // ├─ send(nullptr)  // 通知“没有更多输入”
    // │   ├─ 成功 → flush_sent_cnt_++
    // │   └─ 致命失败 → 打错 & 返回空
    // └─ receive 循环拿 0~N 尾帧
    //     ├─ EnsureSwsReady
    //     └─ ConvertToBGRA → out.push_back()
    // └─ 返回 out
    std::vector<std::shared_ptr<BGRAFrame>> Flush() {
        std::vector<std::shared_ptr<BGRAFrame>> out;
        if (!valid_) return out;

        int ret = avcodec_send_packet(ctx_, nullptr); // 这一步告诉解码器：“没有更多输入了，可以把缓存的帧都吐出来了”
        if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
            PrintFFmpegError("flush send 失败", ret);
            return out;
        }
        if (ret >= 0) ++flush_sent_cnt_; // 单独计入 flush

        // receive 循环拿帧
        while (true) {
            ret = avcodec_receive_frame(ctx_, yuv_);
            if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) break; // 没有更多帧
            if (ret < 0) { PrintFFmpegError("flush receive 失败", ret); break; }

            if (!EnsureSwsReady(yuv_->width, yuv_->height, (AVPixelFormat)yuv_->format)) {
                std::cerr << "[VideoDecoder] 建立/重建 SWS 失败\n";
                continue;
            }
            auto f = ConvertToBGRA(*yuv_);
            if (f) out.push_back(std::move(f));
        }
        if (!out.empty())
            std::cout << "[VideoDecoder] 🔄 Flush 取出 " << out.size() << " 帧\n";
        return out;
    }

    //========================== 统计 ==========================
    // 获取当前解码统计信息。该函数从内部计数器汇总信息，返回一个 Statistics 结构体副本。
    // 用于在运行中查看累计的解码性能数据（包数、帧数、输出字节量、解码效率等）。
    Statistics GetStatistics() const {
        Statistics s{};  // 创建一个新的统计结构体并初始化为 0

        s.total_packets_sent   = sent_cnt_;       // 已成功 send 的真实压缩包数量
        s.flush_packets_sent   = flush_sent_cnt_; // 已成功 send 的 flush 空包数量
        s.total_frames_decoded = frame_cnt_;      // 累计成功解码的帧总数

        // 估算输出的总字节数（frame_cnt * width * height * 4 字节）
        // 注意：若解码过程中分辨率变化，该估算值仅为近似值。
        if (out_w_ > 0 && out_h_ > 0)
            s.total_bytes_out = frame_cnt_ * (int64_t)out_w_ * out_h_ * 4;

        // 计算解码效率（平均每个压缩包产出的帧数）
        // 若还没有成功 send 包，则避免除 0，返回 0.0。
        s.decode_ratio = sent_cnt_ > 0 ? (double)frame_cnt_ / sent_cnt_ : 0.0;

        return s; // 返回统计结果副本
    }

    // 打印当前解码统计信息。
    // 内部调用 GetStatistics() 获取数据，然后调用 Statistics::Print() 输出到控制台。
    // prefix 参数用于在多解码器场景下打印缩进或前缀标识。
    void PrintStatistics() const { GetStatistics().Print("  "); }

    // 获取当前累计解码的帧数量。
    // 相当于解码输出的总帧数（包括 flush 阶段的帧）。
    int64_t GetFrameCount() const { return frame_cnt_; }

    // 获取当前输出帧的宽度（像素）。
    // 通常是最近一次 EnsureSwsReady() 设置的宽度。
    int GetWidth() const { return out_w_; }

    // 获取当前输出帧的高度（像素）。
    // 通常是最近一次 EnsureSwsReady() 设置的高度。
    int GetHeight() const { return out_h_; }


private:
    //========================== 初始化/重建/转换（含 API 解释） ==========================

    /**
     * @brief 初始化解码器
     * 
     * 关键 FFmpeg API 说明：
     * - avcodec_find_decoder(codec_id)
     *   功能：根据 codec_id 查找“解码器描述体”（并不是上下文）。
     * - avcodec_alloc_context3(codec)
     *   功能：根据解码器分配 AVCodecContext（承载解码状态/参数/线程等）。
     * - avcodec_open2(ctx, codec, nullptr)
     *   功能：真正打开解码器（加载默认或给定的私有参数），准备好接收 send/receive。
     *   失败：返回负错误码（编解码器不支持/参数非法/资源不足等）。
     * - av_frame_alloc()
     *   功能：分配 AVFrame 容器（不含像素缓冲）；成功返回 AVFrame*。
     * - sws_getContext(...)
     *   功能：创建像素格式/尺寸转换上下文（YUV→BGRA）；失败返回 nullptr。
     */
    bool Init(const DecoderConfig& cfg) {
        if (cfg.codec_id == AV_CODEC_ID_NONE) {
            std::cerr << "[VideoDecoder] codec_id 无效\n"; return false;
        }
        const int MAX_DIM = 8192;
        if ((cfg.width  > MAX_DIM) || (cfg.height > MAX_DIM)) {
            std::cerr << "[VideoDecoder] 分辨率过大，最大 " << MAX_DIM << "x" << MAX_DIM << "\n";
            return false;
        }

        const AVCodec* codec = avcodec_find_decoder(cfg.codec_id);
        if (!codec) { std::cerr << "[VideoDecoder] 未找到解码器\n"; return false; }
        std::cout << "[VideoDecoder] 使用解码器: " << (codec->long_name?codec->long_name:codec->name) << "\n";

        ctx_ = avcodec_alloc_context3(codec);
        if (!ctx_) { std::cerr << "[VideoDecoder] 分配上下文失败\n"; return false; }

        ctx_->time_base = cfg.time_base;  // 时间基：计算 timestamp 用
        if (cfg.width  > 0) ctx_->width  = cfg.width;   // 允许首帧懒初始化
        if (cfg.height > 0) ctx_->height = cfg.height;

        // 线程策略：0=自动；FF_THREAD_FRAME=尽可能帧级并行
        ctx_->thread_count = 0;
        ctx_->thread_type  = FF_THREAD_FRAME;

        // extradata（如 SPS/PPS）：提升首帧解码成功率
        if (!cfg.extradata.empty()) {
            ctx_->extradata_size = (int)cfg.extradata.size();
            ctx_->extradata = (uint8_t*)av_malloc(ctx_->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
            if (!ctx_->extradata) { std::cerr << "[VideoDecoder] 分配 extradata 失败\n"; return false; }
            std::memcpy(ctx_->extradata, cfg.extradata.data(), cfg.extradata.size());
            std::memset(ctx_->extradata + cfg.extradata.size(), 0, AV_INPUT_BUFFER_PADDING_SIZE);
            std::cout << "[VideoDecoder] 已加载 extradata: " << cfg.extradata.size() << " 字节\n";
        }

        int ret = avcodec_open2(ctx_, codec, nullptr); // 打开解码器
        if (ret < 0) { PrintFFmpegError("打开解码器失败", ret); return false; }

        yuv_ = av_frame_alloc();      // 解码输出帧（YUV/其它）
        tmp_bgra_ = av_frame_alloc(); // SWS 转换的目标承接帧（只存指针/步长）
        if (!yuv_ || !tmp_bgra_) { std::cerr << "[VideoDecoder] 分配帧失败\n"; return false; }

        sws_ = nullptr;               // 延迟创建到首帧后（得到真实宽高/源格式）
        out_w_ = cfg.width;
        out_h_ = cfg.height;
        time_base_ = cfg.time_base;

        return true;
    }

    /// 确保 SWS 准备就绪（源宽高/格式变化时重建）
    bool EnsureSwsReady(int src_w, int src_h, AVPixelFormat src_fmt) {
        if (src_w <=0 || src_h <=0) return false;

        const bool need_recreate =
            (!sws_) || (src_w != out_w_) || (src_h != out_h_) || (src_fmt_last_ != src_fmt);

        if (!need_recreate) return true;

        // 释放旧的 BGRA 缓冲
        if (tmp_bgra_ && tmp_bgra_->data[0]) {
            av_free(tmp_bgra_->data[0]); // 与 av_malloc 成对
            tmp_bgra_->data[0] = nullptr;
        }
        // 释放旧的 SWS
        if (sws_) { sws_freeContext(sws_); sws_ = nullptr; }

        // sws_getContext：创建 YUV→BGRA 的转换上下文
        sws_ = sws_getContext(
            src_w, src_h, src_fmt,
            src_w, src_h, out_pix_fmt_,
            SWS_BILINEAR, nullptr, nullptr, nullptr
        );
        if (!sws_) return false;

        // 为 tmp_bgra_ 分配连续像素缓冲
        int bgra_size = av_image_get_buffer_size(out_pix_fmt_, src_w, src_h, 1);
        uint8_t* buf = (uint8_t*)av_malloc(bgra_size);
        if (!buf) { std::cerr << "[VideoDecoder] 分配 BGRA 缓冲失败\n"; return false; }

        // av_image_fill_arrays：根据像素格式把 data/linesize 填好（指向上面分配的 buf）
        av_image_fill_arrays(
            tmp_bgra_->data, tmp_bgra_->linesize,
            buf, out_pix_fmt_, src_w, src_h, 1
        );

        out_w_ = src_w;
        out_h_ = src_h;
        src_fmt_last_ = src_fmt;

        std::cout << "[VideoDecoder] SWS 就绪: " << out_w_ << "x" << out_h_ << " -> BGRA\n";
        return true;
    }

    /// 把解码得到的 YUV/其它格式转换为 BGRA，并深拷贝到独立缓冲
    std::shared_ptr<BGRAFrame> ConvertToBGRA(const AVFrame& src) {
        // sws_scale：执行像素格式/尺寸转换（这里是 src → tmp_bgra_）
        sws_scale(
            sws_,
            src.data, src.linesize,
            0, src.height,
            tmp_bgra_->data, tmp_bgra_->linesize
        );

        const int stride = tmp_bgra_->linesize[0];
        const size_t sz = (size_t)stride * out_h_;

        // 用 shared_ptr 管理一块连续内存（UI/跨线程使用更安全）
        std::shared_ptr<uint8_t> dst(
            new (std::nothrow) uint8_t[sz],
            [](uint8_t* p){ delete[] p; }
        );
        if (!dst) {
            std::cerr << "[VideoDecoder] 分配帧内存失败: " << sz/1024/1024 << " MB\n";
            return nullptr;
        }
        std::memcpy(dst.get(), tmp_bgra_->data[0], sz);

        auto f = std::make_shared<BGRAFrame>();
        f->data       = std::move(dst);
        f->width      = out_w_;
        f->height     = out_h_;
        f->stride     = stride;
        f->data_size  = sz;
        f->pts        = src.pts;
        f->frame_number = (int)frame_cnt_++;
        if (src.pts != AV_NOPTS_VALUE) f->timestamp = src.pts * av_q2d(time_base_);
        else                           f->timestamp = -1.0;

        return f;
    }

    //========================== 清理/错误 ==========================

    /// 释放所有分配的资源（注意与 av_malloc/av_free 成对）
    void Cleanup() {
        if (tmp_bgra_ && tmp_bgra_->data[0]) {
            av_free(tmp_bgra_->data[0]); // 与 av_malloc 配对
            tmp_bgra_->data[0] = nullptr;
        }
        if (tmp_bgra_) { av_frame_free(&tmp_bgra_); }
        if (yuv_)      { av_frame_free(&yuv_); }
        if (sws_)      { sws_freeContext(sws_); sws_ = nullptr; }
        if (ctx_)      { avcodec_free_context(&ctx_); ctx_ = nullptr; }
    }

    /// 打印 FFmpeg 错误（带错误码→可读字符串）
    void PrintFFmpegError(const std::string& msg, int err) {
        char e[AV_ERROR_MAX_STRING_SIZE]{0};
        av_strerror(err, e, sizeof(e));
        std::cerr << "[VideoDecoder] ❌ " << msg << ": " << e << " (code=" << err << ")\n";
    }

private:
    //========================== 成员变量 ==========================

    AVCodecContext* ctx_;     ///< 解码上下文（承载状态/线程/参数）
    AVFrame* yuv_;            ///< 解码输出帧（源格式，如 yuv420p）
    AVFrame* tmp_bgra_;       ///< SWS 中转帧（指向连续 BGRA 缓冲）
    SwsContext* sws_;         ///< 像素格式转换上下文（YUV→BGRA）

    AVPixelFormat out_pix_fmt_; ///< 目标像素格式（固定 BGRA）
    AVPixelFormat src_fmt_last_;///< 最近一次源像素格式（变化时重建 SWS）

    int64_t frame_cnt_;       ///< 累计输出帧数
    int64_t sent_cnt_;        ///< 累计 send 成功（真实包）
    int64_t flush_sent_cnt_;  ///< 累计 send 成功（flush 空包）

    int out_w_, out_h_;       ///< 当前输出尺寸（随源变化）
    AVRational time_base_;    ///< 时间基（PTS→秒）

    bool valid_;              ///< 初始化是否成功
};

#endif // VIDEO_DECODER_HPP
